#ifndef XTCPSOCKET_HPP
#define XTCPSOCKET_HPP
#include <winsock2.h>
#include<thread>
#include<vector>
#include<iostream>
#include<xsignal/signal.hpp>

class XTcpSocket{
public:
    XTcpSocket(){
        //初始化DLL
        WSADATA wsaData;
        WSAStartup(MAKEWORD(2, 2), &wsaData);
        create();
    };
    
    ~XTcpSocket(){
        if(m_isconnected)
            this->close();
        WSACleanup();
    }
    
//!内部实现接口
private:
    void StatusDetectThrd(){
        //状态监听线程
        fd_set readfds;
		struct timeval tv; 
		tv.tv_sec = 0;                  //设置select()超时时间为2s
		tv.tv_usec = 10;

        char *reciveBuf = new char[2049]{};
        while (m_isconnected)
        {
            if (m_isconnected == true)
            {
                FD_ZERO(&readfds);
                FD_SET(m_clientSock, &readfds);

                int selectRet = select(0, &readfds, nullptr, nullptr, &tv);
                if (selectRet > 0)
                {
                    if (FD_ISSET(m_clientSock, &readfds))
                    {
                       int recvRet = recv(m_clientSock, reciveBuf, 2048, 0);
                       if(recvRet ==0){
                            xemit(sig_disconnect)();
                            m_isconnected = false;      //同时结束自身循环
                       }else{
                            xemit(sig_read)(reciveBuf,recvRet);
                       }  
                    }
                }
                else if (selectRet == 0)
                {
                    // 超时
                }
                else
                {
                    std::cout << "client: select() error" << std::endl;
                    m_isconnected = false;
                }
            }else{
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
    }

    bool AsyncConnect(std::string ip,unsigned int port){
        
        struct sockaddr_in sockAddr;
        memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
        sockAddr.sin_family = PF_INET;
        sockAddr.sin_addr.s_addr = inet_addr(ip.data());
        sockAddr.sin_port = htons(port);

        //启动连接监听线程
        fd_set writefds,expectfds;
		struct timeval tv; 
		tv.tv_sec = 2;//设置select()超时时间为2s
		tv.tv_usec = 0;
		
        int res = ::connect(m_clientSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
        if (res == 0){
            std::cout << "client: connect directly success!" << std::endl;
            m_isconnected = true;
            xemit(sig_connect)();
            return true;
        }else if (res == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
        {
            std::cout << "can not connect to server, error: " << WSAGetLastError() << std::endl;
            return false;
        }

        FD_ZERO(&writefds);
        FD_ZERO(&expectfds);
        FD_SET(m_clientSock, &writefds);
        FD_SET(m_clientSock, &expectfds);

        int selectRet = select(0, NULL, &writefds, nullptr, &tv);
        if (selectRet > 0)
        {
            if (FD_ISSET(m_clientSock, &writefds)){
                //结束上一次的监测线程
                if(m_detectThrd.joinable()){
                    m_detectThrd.join();
                }
                m_isconnected = true;
                //连接成功启动状态监测线程
                m_detectThrd = std::thread(&XTcpSocket::StatusDetectThrd,this);
                xemit(sig_connect)();
                return true;
            } 
            return false;
        }
        else if (selectRet == 0)
        {
            std::cout << "client: connect timeout" << std::endl;
            return false;
        }
        else
        {
            std::cout << "client: select() error" << std::endl;
            return false;
        }
    }

//!外部调用接口
public:
    //创建套接字
    bool create(){
        //创建套接字
        m_clientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(m_clientSock == INVALID_SOCKET)
            return false;
        //设为非阻塞模式
        ULONG NonBlock = 1; 
        ioctlsocket(m_clientSock, FIONBIO, &NonBlock);
        return true;
    }
    //连接到服务器
    void connectToServer(std::string ip,unsigned int port){
        m_connectThrd = std::thread(&XTcpSocket::AsyncConnect,this,ip,port);
    }
    //关闭socket
    void close(){
        if(m_isconnected){
            ::closesocket(m_clientSock);
        }
    }
    //写数据1
    void write(std::vector<char> buf){
        if(m_isconnected == false)
            return;
        ::send(m_clientSock, &buf[0], buf.size(), 0);
    }
    //写数据2
    void write(std::string buf){
        if(m_isconnected == false)
            return;
        ::send(m_clientSock, buf.data(), buf.size(), 0);
    }
    //获取socketID
    SOCKET getsocketId() const {return m_clientSock;}

private:
    SOCKET m_clientSock = 0;
    bool m_isconnected = false;
    std::thread m_connectThrd;
    std::thread m_detectThrd;
public:
     XSIGNAL(sig_connect);
     XSIGNAL(sig_disconnect);
     XSIGNAL(sig_read,char*,int);
};
#endif